游戏开发中的Vector3运算法则的优化

您所在的位置:网站首页 unity operator 游戏开发中的Vector3运算法则的优化

游戏开发中的Vector3运算法则的优化

2024-01-25 07:08| 来源: 网络整理| 查看: 265

写在前面:游戏中很多地方都会涉及到数学运算,而在数学运算中,也是有运算效率的区分的,加(+)减法(-)是最快的,其次是乘法(*)和取余(%),接着才是除法(/),开根号(√)的运算会比前面几者都要慢。所以本文章主要处理开根号和除法,尽量在游戏开发中避免。

一、Vector3.Distance优化

经常在游戏开发中会用到计算两个点的距离,当距离小于或者大于某个值时做什么做什么事,多用于小怪的AI计算,但是,这种计算Distance的数学公式是:

距离 = 两点每个分量的差值相加,并开根号。

float Vector3.Distance(Vector3 a,Vector b) { float sqr_x = (a.x - b.x) * (a.x - b.x); float sqr_y = (a.y - b.y) * (a.x - b.x); float sqr_z = (a.z - b.z) * (a.z - b.z); return Mathf.Sqrt(sqr_x + sqr_y + sqr_z); }

会发现,该运算会使用开根号的方法去计算两个点之间的距离。需要做的优化方法也很简单,我们不使用开根号后的值,我们只使用开根号前的值。

扩张下面这个方法:

float SqrDistance(Vector3 a,Vector b) { float sqr_x = (a.x - b.x) * (a.x - b.x); float sqr_y = (a.y - b.y) * (a.x - b.x); float sqr_z = (a.z - b.z) * (a.z - b.z); return sqr_x + sqr_y + sqr_z; }

那么在判断只需要这么修改:

if(SqrDistance(point1,point2) < minDistance * minDistance) { //DoSomething... }  二、除法的优化

除法的优化,是针对于除数是已知的条件下,那么可以相对应的优化。先提前计算好1/n的值后,然后用乘法去乘上计算出来的1/n的值即可。

举个栗子,已知一个变量 float a = 2.333f;我想计算 a/4的值: 

用计算器可以算出1/4=0.25;  所以要计算a/4就变成了 

float a = 2.333f; float value = a * 0.25f;//代替a/4 三、向量夹角优化

我们经常用扇形来做是否攻击到或者做小怪AI侦查,用扇形来进行判断。那么就一定要进行夹角的计算。

扇形有个角度,所以会先判断角度,再判断距离。

因为扇形有个朝向,所以会用到transform.forward。

如下图所示:

所以默认代码将会如下:

//forward是扇形朝向,一般是就是人物朝向(forward本身就是单位向量) //pos是人物位置 //target是要计算的目标位置 //angle是攻击的角度范围,这是总的角度范围,需要除以2来求出一边的角度。 //distance是扇形的距离 bool CheckFanShape(Vector3 forward,Vector3 pos,Vector3 target,float angle,float distance) { Vector3 target_dir = (target - pos).normalize;//算出当前点目标点的向量 float _angle = Vector3.Angle(target_dir, forward);//计算出人物朝向跟目标朝向的角度。用unity的api Vector3 _disntace = Vector3.Distance(pos,target);//计算两点之间的距离 return _angle cosθ2  

前半部分很好计算,主要是后面的cosθ2,这个三角函数怎么简化呢? (所有的三角函数,sinθ cosθ  ,因为都是固定的一个数,所以我们可以记好,然后通过读表的方式读取sin和cos的值。一般请教下,θ值都是int就足够了,如果θ 值需要有小数点,那就扩大θ 的精度,表格中保存更多的θ 值。)

我们就通过读取表格的方式算出了cosθ2的值。

写成代码就是:

Dictionary cosTable = new Dictionary(); float Utility.Cosf(int angle) { return cosTable[angle]; } Vector3 Utility.Normalize(Vector3 dir) { return dir.normalize;//这里留个悬念,后面会讲到这个normalize的优化。 } bool CheckAngle(Vector3 forward,Vector3 pos,Vector3 target,int angle) { //因为这个算的是2D的,所以我们取的是x和z的值 Vector3 dir = Utility.Normalize(target - pos); float cos_value = forward.x * forward.z + dir.x * dir.z; float target_cos_value = Utility.Cosf(angle);//注意,这里的angle是除以2以后的。 return cos_value > target_cos_value; }

这样我们就完成了角度的检测优化,用来判断角度是否小于一定的角度,而且策划想配置几度就几度,因为我们的cos计算是直接读表取出的。

上面之前说到,必须要是单位向量,才能把cosθ后面的/(|a||b|)给约掉,因为长度都为1,不管任何数,除以1都是本身。

那么,向量长度变为1,我们俗称的归一化,有什么好的好的优化呢?下面就讲到了。

四、Normalize的优化计算

正常情况下,我们需要将向量归一化,那么就必须计算出向量的长度,然后当前向量去除以本身长度,就求出了归一化后的向量了。

向量.normalize = 向量 / 向量.Distance

代码如下:

Vector3 Normalize(Vector3 dir) { float sqr_len = dir.x *dir.x + dir.y * dir.y + dir.z * dir.z; return dir / Mathf.Sqrt(sqr_len); }

可以发现,我们不仅要用上除法,还要计算平方根,而且,这时我们的第一个优化distance的办法就不适用了,因为我们必须计算平方根进行除法。那怎么办呢?

所以我们需要对除法加上开根号同时优化,也就是说,如果我们可以把 1/ Mathf.Sqrt(n) 的值求出,那么Normalize就等价于:

dir * value

那么这个value公式怎么求呢?

//这个函数就是用来计算1/sqr(x)的 float InvSqrt (float x)  {     float xhalf = 0.5f*x;     int i = *(int*)&x;     i = 0x5f3759df - (i>>1);     x = *(float*)&i;     x = x*(1.5f - xhalf*x*x);     return x; }

这个函数就能很方便计算出1/ Mathf.Sqrt(n)的值。这个函数是需要操作指针。所以在C#这个有语言里算是不安全的代码。得加上unsafe标签,并且要设置允许unsafe代码。

五、其他一些在项目中可以会经常用到的Vector3公式

①计算多个点的中心点位置。

原理很简答,就是把所有点相加,然后除以一个这些点的总个数,就能算出中心点了。

Vector3 GetCenter(Vector3[] points) { Vector3 center = Vector3.zero; for (int i = 0; i < points.Length; i++) { center += points[i]; } return center / points.Length; }

②计算一个点位于一条向量是左边还是右边。(这里是基于2D平面的,所以用Vector2代替,如果是3D的,就没法定义左边右边的含义了。)

原理是:向量a.x * 向量b.y - 向量a.y * 向量b.x

enum MyDirectionType//所在方向的偏向哪一边 { Left, InLine, Right } //start 是向量的开始位置 //end 是向量的结束位置 //calPoint 是要计算的目标点 MyDirectionType GetDirection(Vector3 start,Vector3 end,Vector3 calPoint) { //根据3个点的参数,计算出两条向量 Vector3 dir1 = end - start; Vector3 dir2 = calPoint - start; float value = dir1.x * dir2.y - dir1.y * dir2.x; if (value < 0) { return MyDirectionType.Right; } else if (value == 0) { return MyDirectionType.InLine; } else { return MyDirectionType.Left; } }

这个方法多用在一些顶点计算,计算某个点是否在一个三角形内部,如果三角形三个点构成的向量跟目标点的方向都是同一侧,都是居左或者都是居右,那就说明该点在三角形内部。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3